<?PHP
/*
 * $Id: barge-shell.classes.php 139 2009-12-16 15:29:56Z namba0219 $
 * =============================================================================
 * Barge - Web application framework on PHP5 
 *  
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * =============================================================================
 * For more information, see http://barge.googlecode.com/
 */
namespace barge\shell;

use \Exception;

use barge\System;
use barge\logging\LogFactory;

/**
 *
 * @author Kenji Namba 
 * @version 1.0
 * @since Barge 1.0 2006/08/12
 * @see Exception
 */
class ShellException extends Exception 
{
}

/**
 *
 * @author Kenji Namba 
 * @version 1.0
 * @since Barge 1.0 2006/08/12
 */
class Shell 
{
	/** @var string */
	private $inputEncoding = null;

	/** @var string */
	private $outputEncoding = null;
	
	/**
	 * @param string $comand
	 * @param string $input
	 * @return int
	 * @throws ShellException
	 */
	public function exec($command, $input = null)
	{
        $descriptorspec = array(
            1 => array('pipe', 'w'),
            2 => array('pipe', 'w')
        );
        if (!empty($input))
        	$descriptorspec[0] = array('pipe', 'r');

		$pipes = null;
$log = LogFactory::getLogger();
//$log->debug('proc_open : '.$command);
        $resource = proc_open($command, $descriptorspec, $pipes, System::getBaseDir());
        if (!is_resource($resource)) {
        	throw new ShellException('Cannot open command');
        }

		foreach ($pipes as $pipe)
        	stream_set_blocking($pipe, false);
        
        if (!empty($input))
			$stdin = $pipes[0];
		$stdout = $pipes[1];
		$stderr = $pipes[2];

		// 先にすべての入力を注入
		if (!empty($input))
		{
			if ($this->inputEncoding !== null) {
				$input = mb_convert_encoding($input, $this->inputEncoding, 'utf-8');
			}
			stream_set_timeout($stdin, 3);
			fwrite($stdin, $input);
			fclose($stdin);
		}

		// その後すべての出力を取得
		$retval = '';
		$error = '';
        $read = array($stdout, $stderr);
        $write = null;
        $except = null;
        for ($i = 0; (!feof($stdout) || !feof($stderr)) && $i < 100; ++$i) {
        	$ret = stream_select($read, $write, $except, 0);
			if ($ret === false || $ret === 0) break;
	        foreach ($read as $sock) {
	            if ($sock === $stdout) {
	                $retval .= fread($sock, 4096);
	            }
	            else if ($sock === $stderr) 
	                $error .= fread($sock, 4096);
	        }
		}

		fclose($stdout);
		fclose($stderr);
		
		$exit_code = proc_close($resource);
        if (!empty($error)) {
        	if ($this->outputEncoding !== null) {
				$error = mb_convert_encoding($error, 'utf-8', $this->outputEncoding);
			}
			
            throw new ShellException($error);
        } else {
        	if ($this->outputEncoding !== null) {
				$retval = mb_convert_encoding($retval, 'utf-8', $this->outputEncoding);
			}
            return $retval;
        }
	}
	
	/**
	 * @param string $value
	 */
	public function setInputEncoding($value)
	{
		$this->inputEncoding = $value;
	}
	
	/**
	 * @param string $value
	 */
	public function setOutputEncoding($value)
	{
		$this->outputEncoding = $value;
	}
}


